package org.elephantt.webby;

import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.*;
import org.mortbay.thread.BoundedThreadPool;
import org.mortbay.thread.ThreadPool;

/**
 * An HTTP-based application. Useful as a base class if your application is primarily an HTTP-serving application.
 * Currently uses Jetty. If you have very simple needs, see SimpleHttpApplication.
 *
 * @see org.elephantt.webby.SimpleHttpApplication
 */
public abstract class HttpApplication {
  private final Server server;

  public RequestMapper createRequestMapper() {
    return new RouteRequestMapper();
  }

  public abstract RequestResponseFactory createRequestResponseFactory();

  public Handler createDispatchHandler() {
    return new WebbyDispatchHandler(createDispatcher());
  }

  public Dispatcher createDispatcher() {
    return new Dispatcher(createRequestMapper(), createRequestResponseFactory());
  }

  protected HttpApplication(HttpApplicationConfig config) {
    server = createJettyServer(config);
  }

  private Server createJettyServer(HttpApplicationConfig config) {
    Server s = new org.mortbay.jetty.Server(config.getPort());

    ContextHandler appContext = new ContextHandler();
    appContext.setContextPath("/");
    Handler appHandler = createDispatchHandler();
    appContext.setHandler(appHandler);
    appContext.setErrorHandler(null); // TODO: standard error handler

    ContextHandlerCollection contexts = new ContextHandlerCollection();
    contexts.setHandlers(new Handler[]{appContext});

    HandlerCollection handlers = new HandlerCollection();
    RequestLogHandler requestLogHandler = new RequestLogHandler();
    handlers.setHandlers(new Handler[]{contexts, new DefaultHandler(), requestLogHandler});

    s.setHandler(handlers);

    s.setThreadPool(createThreadPool(config));

    return s;
  }

  public ThreadPool createThreadPool(HttpApplicationConfig config) {
    BoundedThreadPool threadPool = new BoundedThreadPool();
    threadPool.setMinThreads(config.getMinThreads());
    threadPool.setMaxThreads(config.getMaxThreads());
    threadPool.setDaemon(true);
    return threadPool;
  }

  public void start() throws Exception {
    server.start();
  }

  /**
   * Starts the server and joins, such that this method does not return. 
   * @throws Exception
   */
  public void startAndJoin() throws Exception {
    server.start();
    server.join();
  }

  public void stop() throws Exception {
    server.stop();
  }
}
